/*
 *  MicroEmulator
 *  Copyright (C) 2001,2002 Bartek Teodorczyk <barteo@barteo.net>
 *
 *  It is licensed under the following two licenses as alternatives:
 *    1. GNU Lesser General Public License (the "LGPL") version 2.1 or any newer version
 *    2. Apache License (the "AL") Version 2.0
 *
 *  You may not use this file except in compliance with at least one of
 *  the above two licenses.
 *
 *  You may obtain a copy of the LGPL at
 *      http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
 *
 *  You may obtain a copy of the AL at
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the LGPL or the AL for the specific language governing permissions and
 *  limitations.
 *  
 *  @version $Id: SwingDeviceComponent.java 1719 2008-05-20 04:16:53Z vlads $ 
 */

package org.microemu.app.ui.swing;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.KeyboardFocusManager;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;

import org.microemu.DeviceComponent;
import org.microemu.DisplayComponent;



public class SwingDeviceComponent extends JPanel 
                    implements DeviceComponent, KeyListener, FocusListener {

    private SwingDeviceComponentConcreteView view = null;
	private static final long serialVersionUID = 1L;

    
    private static final int VIBRATION_TIMER_INTERVAL_PERIOD_IN_MILLISECONDS = 25; 
    private static final int VIBRATION_DELTA_IN_PIXELS = 2;
    	
	private boolean vibrationEnabled = false;
	private Timer vibrationTimer = null;
	
	
	private GridBagLayout layoutManager = new GridBagLayout();
	private GridBagConstraints layoutConstraints = new GridBagConstraints();


	public SwingDeviceComponent(boolean enableVibration) {
	    // empty border
	    setBorder( new EmptyBorder(0,0,0,0) );
	    
	    // setup my layout
	    setLayout( layoutManager );
	    
		// vibration enabled?
	    vibrationEnabled = enableVibration;
 
        // setup constraints
        if (vibrationEnabled)
        {
            layoutConstraints.insets.left = VIBRATION_DELTA_IN_PIXELS;
            layoutConstraints.insets.right = VIBRATION_DELTA_IN_PIXELS;
        }
	    
        view = SwingDeviceComponentConcreteView.getInstance( this );
        layoutManager.setConstraints( view, layoutConstraints );
        add(view);	
        
        // set bground color
        setBackground( UIManager.getColor( "text" ) );
        
        addFocusListener( this );
        //addKeyListener( this );
	}

	
	public DisplayComponent getDisplayComponent() {
		return view.getDisplayComponent();
	}

	public void init() {
		view.init();
	}
	


	public void switchShowMouseCoordinates() {
		view.switchShowMouseCoordinates();
	}

	/**
	 * Used by {@link SwingDisplayComponent}
	 */
	public void keyTyped(KeyEvent ev) {
		view.keyTyped( ev );
	}

    /**
     * Used by {@link SwingDisplayComponent}
     */
	public void keyPressed(KeyEvent ev) {
		view.keyPressed( ev );
	}

    /**
     * Used by {@link SwingDisplayComponent}
     */
	public void keyReleased(KeyEvent ev) {
		view.keyReleased( ev );
	}

	public MouseListener getDefaultMouseListener() {
		return view.getDefaultMouseListener();
	}

	public MouseMotionListener getDefaultMouseMotionListener() {
		return view.getDefaultMouseMotionListener();
	}


	public Dimension getPreferredSize() {
	    Dimension d = view.getPreferredSize();

	    if (vibrationEnabled)
	    {
	        d.width += layoutConstraints.insets.left + 
	                        layoutConstraints.insets.right;
	    }
	    
	    return d;
	}



    /**
	 * @see DeviceComponent#doVibrate(int)
	 */
    public boolean doVibrate( int duration )
    {
        // vibration enabled?
        if (!vibrationEnabled)
            return false;
        
        synchronized(SwingDeviceComponent.this)
        {
            // turn vibration off
            //
            // TODO:    at the moment we turn it off permanently, but is this 
            //          is the correct interpretation of the spec?
            //
            if (0 >= duration)
            {
                if (null != vibrationTimer)
                {
                    vibrationTimer.cancel();
                    vibrationTimer = null;
                }
            }
            else
            {
                // if not already vibrating
                if (null == vibrationTimer)
                {
                    if (duration < VIBRATION_TIMER_INTERVAL_PERIOD_IN_MILLISECONDS)
                    {
                        System.out.println("Unable to vibrate for " + duration + " ms. Please supply value >= " + VIBRATION_TIMER_INTERVAL_PERIOD_IN_MILLISECONDS + " ms");
                    }
                    else
                    {
                        System.out.println("About to vibrate for " + duration + " ms...");
                    }
                    
                    // kick off a new timer
                    vibrationTimer = new Timer();
                    vibrationTimer.schedule( 
                            new VibrationTask(duration/VIBRATION_TIMER_INTERVAL_PERIOD_IN_MILLISECONDS), 
                            0, 
                            VIBRATION_TIMER_INTERVAL_PERIOD_IN_MILLISECONDS );
                }
            }
        }
        
        return true;
    }

    
    private class VibrationTask extends TimerTask
    {
        private int numberOfTimesIShouldRun = 0;
        private int numberOfTimesIHaveRun = 0;
        
        private boolean onLeft = false;
        
        public VibrationTask(int numTimesToRun)
        {
            numberOfTimesIShouldRun = numTimesToRun;
        }
        
        public void run()
        {
            // done vibrating?
            if (++numberOfTimesIHaveRun > numberOfTimesIShouldRun)
            {
                synchronized(SwingDeviceComponent.this)
                {
                    this.cancel();
                    vibrationTimer.cancel();
                    vibrationTimer = null;
                }
                
                // reset display
                layoutConstraints.insets.left = VIBRATION_DELTA_IN_PIXELS;
                layoutConstraints.insets.right = VIBRATION_DELTA_IN_PIXELS;
            }
            else
            {
                // if on left-hand side
                if (onLeft)
                {
                    // move to right
                    layoutConstraints.insets.left = VIBRATION_DELTA_IN_PIXELS * 2;
                    layoutConstraints.insets.right = 0;
                }
                else
                {
                    // move to left
                    layoutConstraints.insets.left = 0;
                    layoutConstraints.insets.right = VIBRATION_DELTA_IN_PIXELS * 2;
                }
                
                onLeft = !onLeft;
            }
            
            // redo layout
            layoutManager.setConstraints( view, layoutConstraints );
            revalidate();
            
            // now update the display
            repaint();                
        }
    }


    /**
     * BlueWhaleSystems fix: Ramesh Nair - 1 May 2008
     * 
     * We need to track when do and don't have keyboard focus. This helps us 
     * determine when the text cursor should be shown.
     * 
     * See ticket #1356
     */     
    public void focusGained( FocusEvent e )
    {
        view.focusGained( e );
    }


    /**
     * BlueWhaleSystems fix: Ramesh Nair - 1 May 2008
     * 
     * We need to track when do and don't have keyboard focus. This helps us 
     * determine when the text cursor should be shown.
     * 
     * See ticket #1356
     */     
    public void focusLost( FocusEvent e )
    {
        view.focusLost( e );
    }
}
